%% Copyright
-module(review_handler).
-author("").

%% API
-export([init/3, allowed_methods/2, content_types_provided/2, content_types_accepted/2,
         add_or_update_review/2, get_review/2]).

init(_Transport, _Req, []) ->
  {upgrade, protocol, cowboy_rest}.

allowed_methods(Req, State) ->
  {[<<"GET">>, <<"PUT">>, <<"POST">>], Req, State}.

content_types_accepted(Req, State) ->
  {[{{<<"application">>, <<"json">>, []}, add_or_update_review}],
    Req, State}.

content_types_provided(Req, State) ->
  {[{{<<"application">>, <<"json">>, []}, get_review}],
    Req, State}.

add_or_update_review(Req, State) ->
  {ProductId, Req1} = cowboy_req:binding(product_id, Req),
  {ok, Body, Req2} = cowboy_req:body(Req1),
  case jsx:is_json(Body) of
    true -> add_or_update_review(Req2, State, ProductId, Body);
    false ->
      {ok, Req3} = cowboy_req:reply(415, Req2),
      {ok, Req3, State}
  end.

add_or_update_review(Req, State, ProductId, Body) ->
  case cn2p_catalog:product_exists(ProductId) of
    true -> check_method_and_process(Req, State, ProductId, Body);
    false -> http_util:get_product_not_found_response(Req, State, ProductId)
  end.

check_method_and_process(Req, State, ProductId, Body) ->
  case cowboy_req:method(Req) of
    {<<"POST">>, Req1} -> add_review(Req1, State, ProductId, jsx:decode(Body));
    {<<"PUT">>, Req1} ->
      case cowboy_req:binding(review_id, Req1) of
        {undefined, Req2} ->
          {ok, Req3} = cowboy_req:reply(400, [], [], Req2),
          {ok, Req3, State};
        {ReviewId, Req2} -> update_review(Req2, State, ProductId, ReviewId, jsx:decode(Body))
      end
  end.

add_review(Req, State, ProductId, Review) ->
  {ok, ReviewId} = cn2p_catalog:add_product_review(ProductId, Review),
  LocationHeader = {<<"Location">>, get_review_url(ProductId, ReviewId)},
  {ok, Req1} = cowboy_req:reply(201, [LocationHeader], [], Req),
  {ok, Req1, State}.

update_review(Req, State, ProductId, ReviewId, ReviewUpdate) ->
  case cn2p_catalog:review_exists(ReviewId) of
    true ->
      {ok, ReviewId} = cn2p_catalog:update_product_review(ProductId, ReviewId, ReviewUpdate),
      LocationHeader = {<<"Location">>, get_review_url(ProductId, ReviewId)},
      {ok, Req1} = cowboy_req:reply(201, [LocationHeader], [], Req),
      {ok, Req1, State};
    false ->
      http_util:get_review_not_found_response(Req, State, ReviewId)
  end.

get_review(Req, State) ->
  {ProductId, Req1} = cowboy_req:binding(product_id, Req),
  {{PageIndex, PageSize}, Req2} = http_util:get_paging_parameters(Req1),

  case cn2p_catalog:product_exists(ProductId) of
    true ->
      case cowboy_req:binding(review_id, Req2) of
        {undefined, Req3} -> get_reviews_response(Req3, State, ProductId, PageIndex, PageSize);
        {ReviewId, Req3} -> get_review_response(Req3, State, ProductId, ReviewId)
      end;
    false ->
      http_util:get_product_not_found_response(Req2, State, ProductId)
  end.

get_review_response(Req, State, ProductId, ReviewId) ->
  case cn2p_catalog:get_product_review(ProductId, ReviewId) of
    {ok, Review} -> {jsx:encode(Review), Req, State};
    {error, not_found} ->
      http_util:get_review_not_found_response(Req, State, ReviewId)
  end.

get_reviews_response(Req, State, ProductId, PageIndex, PageSize) ->
  case cn2p_catalog:get_product_reviews(ProductId, PageIndex, PageSize) of
    {ok, Reviews} -> {jsx:encode(Reviews), Req, State};
    {error, not_found} ->
      http_util:get_product_not_found_response(Req, State, ProductId)
  end.

get_review_url(ProductId, ReviewId) ->
  {ok, UrlBase} = application:get_env(cn2p_product_catalog_service, service_uri),
  <<(list_to_binary(UrlBase))/binary, "/api/", ProductId/binary, "/reviews/", ReviewId/binary>>.